<template>
  <el-dialog
    v-model="renderVisible"
    :title="props.title"
    :width="props.size"
    append-to-body
    destroy-on-close
    :before-close="handleBeforeClose"
  >
    <el-upload
      ref="uploadRef"
      class="upload-demo"
      drag
      :name="props.field"
      :action="props.action"
      :method="props.method"
      :headers="headers"
      :on-success="handleUploadSuccess"
      :on-error="handleUploadError"
      :before-upload="handleUploadBeFore"
      :auto-upload="false"
      :limit="props.max"
      :disabled="isDisabled"
      multiple
    >
      <el-icon class="el-icon--upload"><upload-filled /></el-icon>
      <div class="el-upload__text">拖拽文件 或 <em>点击上传</em></div>
      <template #tip>
        <div class="el-upload__tip">
          <span style="margin-right: 16px"
            >文件格式：{{ getUsableTypes.join('/') }}</span
          >
          <span style="margin-right: 16px"
            >文件大小(MB)：{{ props.usableSize }}</span
          >
          <span style="color: #f56c6c">文件数量：{{ props.max }}</span>
        </div>
      </template>
    </el-upload>

    <template #footer>
      <el-button type="primary" @click="handleClickSubmit()"
        >上传图片</el-button
      >
    </template>
  </el-dialog>
</template>

<script setup lang="ts">
import { UploadFilled } from '@element-plus/icons-vue';
import { computed, PropType, ref, unref, watch } from 'vue';
import configs from '@/utils/configs';
import { localtoken } from '@/utils/util';
import router from '@/router';
import type {
  // NotificationHandle,
  UploadFile,
  UploadFiles,
  // UploadProgressEvent,
  UploadProps,
  UploadRawFile,
  // UploadUserFile,
  UploadInstance
} from 'element-plus';
import * as imageConversion from 'image-conversion';
import { ElMessage } from 'element-plus';
import { ElNotification } from 'element-plus';

// 给每个接口插入token
let token = localtoken.get();
const headers: any = {
  Authorization: `Bearer ${token}`
};
// if (token && token != "null") {
//   headers["Authorization"] = `Bearer ${token}`;
//   console.log(headers)
// }

// props
const props = defineProps({
  visible: {
    type: Boolean,
    default: false
  },

  // 控件类型
  type: {
    type: String,
    default: 'image',
    validator(value: string) {
      return ['image', 'video'].includes(value);
    }
  },

  list: {
    type: Array as PropType<string[]>,
    default: () => []
  },

  title: {
    type: String,
    default: '上传图片'
  },

  size: {
    type: [String, Number],
    default: '50%'
  },

  // 最大上传数量
  max: {
    type: Number,
    default: 1
  },

  // 上传字段
  field: {
    type: String,
    default: 'file'
  },

  // 上传地址
  action: {
    type: String,
    default: configs.API_BASE_URL + '/upload/upload'
  },

  // 请求类型
  method: {
    type: String,
    default: 'post'
  },

  /**
   * 可用的文件类型
   *
   * @example ["text/csv", "text/plain", "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
   */
  usableMimes: {
    type: Array as PropType<Array<string>>
  },

  /**
   * 可用的文件格式
   *
   * @example ["txt", "excel", "csv", "xlsx", "xls", "xlsm", "xlsb"]
   */
  usableTypes: {
    type: Array as PropType<Array<string>>
  },

  // 文件大小(MB)
  usableSize: {
    type: Number,
    default: 50
  },

  // 压缩后的质量大小（KB），不传则不压缩
  compressSize: {
    type: Number,
    default: 0
  }
});

// emits
const emits = defineEmits(['update:visible', 'onComplete']);

// 数据列表
const dataList = ref<string[]>([]);
watch(
  () => props.list,
  (val) => {
    dataList.value = [...val];
  },
  {
    immediate: true
  }
);

// 是否可上传
const isDisabled = computed(() => props.max <= 0);

// computed - 抽屉[显示|隐藏]
const renderVisible = computed({
  get: () => props.visible,
  set: (val) => {
    emits('update:visible', val);
  }
});

// computed - 可用的文件类型
const getUsableMimes = computed<string[]>(() => {
  // 如果通过props传入，则直接使用
  if (props.usableMimes) return props.usableMimes;

  switch (props.type) {
    case 'image':
      return ['image/jpeg', 'image/png'];
    case 'video':
      return ['video/mp4'];
    default:
      return ['image/jpeg', 'image/png'];
  }
});

// computed - 可用的文件格式
const getUsableTypes = computed<string[]>(() => {
  // 如果通过props传入，则直接使用
  if (props.usableTypes) return props.usableTypes;

  switch (props.type) {
    case 'image':
      return ['jpg', 'jpeg', 'png'];
    case 'video':
      return ['mp4'];
    default:
      return ['jpg', 'jpeg', 'png'];
  }
});

// 上传文件 - 上传前
const handleUploadBeFore: UploadProps['beforeUpload'] = (
  rawFile: UploadRawFile
): any => {
  // 检查文件格式
  const isUsable = getUsableMimes.value.includes(rawFile.type);

  // 检查文件大小
  const size_mb = (rawFile.size / 1024 / 1024).toFixed(2);
  const isSize = Number(size_mb) <= props.usableSize;

  // 判断是否通过所有前置判断
  const judge = isUsable && isSize;

  // 判断是否需要压缩
  if (!props.compressSize || props.type !== 'image') {
    return judge;
  }

  // 压缩
  return new Promise((resolve) => {
    // 压缩到{{props.compressSize}}KB,这里的props.compressSize就是要压缩的大小,可自定义
    imageConversion
      .compressAccurately(rawFile, props.compressSize)
      .then((res) => {
        resolve(res);
      });
  });
};

// 上传成功
const handleUploadSuccess: UploadProps['onSuccess'] = (
  response: any,
  _uploadFile: UploadFile,
  _uploadFiles: UploadFiles
) => {
  const { path, name }: { [key: string]: string } = response;
  dataList.value.push(path + '/' + name);
};

// 上传错误
const handleUploadError: UploadProps['onError'] = (
  error: Error | any,
  _uploadFile: UploadFile,
  _uploadFiles: UploadFiles
) => {
  if (error.status) {
    if (error.status === 401) {
      // 关闭UploadModal
      emits('update:visible', false);
      localtoken.del();
      ElNotification({
        title: 'Token失效',
        message: '单次登录Token已失效，请重新登录！',
        duration: 0,
        type: 'error'
      });
      router.push({ path: '/login' });
      return;
    }
  }

  ElMessage.error(error.message);
};

// 上传组件ref
const uploadRef = ref<UploadInstance>();

// btn - submit
const handleClickSubmit = () => {
  uploadRef.value!.submit();
};

/**
 * 监听关闭
 *
 * @param done
 */
const handleBeforeClose = (done: () => void) => {
  const result = unref(dataList);

  emits('onComplete', {
    action: 'close',
    result
  });

  done();
};
</script>
